package yxy.cherry.data.input;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import org.redisson.api.RBucket;
import org.redisson.api.RList;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import yxy.apple.logger.Logger;
import yxy.apple.util.FileUtility;
import yxy.apple.util.JSONUtility;
import yxy.apple.util.MD5Utility;
import yxy.apple.util.ReflectionUtility;
import yxy.apple.util.StringUtillity;
import yxy.apple.util.redisson.FastjsonCodec;
import yxy.apple.util.redisson.RedisConnection;
import yxy.apple.util.redisson.RedisException;
import yxy.banana.launcher.Reactable;
import yxy.cherry.data.bean.model.DataClass;
import yxy.cherry.data.dfa.Dfa;

public class Reactor implements Reactable<Configuration> {

	protected static Logger logger = Logger.create(Reactor.class);

	public static final Reactor instance = new Reactor();

	@Override
	public void start(Configuration configuration) throws Exception {
		// 启动配置 config

		// 获取redis连接，是否能连接
		RedisConnection redis = new RedisConnection();
		try {
			redis.connect(configuration.Redis.Address, configuration.Redis.Password, configuration.Redis.Database, true, new FastjsonCodec());
		} catch (RedisException e) {
			e.printStackTrace();
			logger.error("Redis服务器连接错误");
			throw e;
		}
		RedissonClient client = redis.client();

		redisData(client, configuration);// json

		redisJs(client, configuration);// Fomula

		redisSensitive(client, configuration);// 敏感词

		redisAct(client, configuration, 0);
		
		redis.disconnect();
	}

	/** 敏感词 */
	private void redisSensitive(RedissonClient client, Configuration configuration) throws Exception {

		RList<String> rlist = client.getList("SENSITIVE:WORDS");

		ArrayList<String> list = new ArrayList<>();

		File file = new File(System.getProperty("user.dir") + "/data/sensitive.txt");

		RBucket<String> rbucket = client.getBucket("SENSITIVE:MD5");
		String md5 = null;
		try {
			md5 = MD5Utility.getMD5(file);
			//md5值一样，则不用处理
			if (rbucket.isExists() && rbucket.get().equals(md5)) {
				return;
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("md5解析错误：%s", e.getMessage());
			throw e;
		}
		//md5值不一致，则更新
		rbucket.set(md5);
		rlist.clear();

		BufferedReader reader = new BufferedReader(new FileReader(file));
		String line;
		while ((line = reader.readLine()) != null) {
			line = StringUtillity.toDBC(line).trim().toLowerCase();
			if (line.length() <= 0) {
				continue;
			}
			list.add(line);
		}
		reader.close();

		list.sort(new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.length() - o2.length();
			}
		});

		Dfa dfa = new Dfa();

		HashSet<String> resultSet = new HashSet<>();

		for (String word : list) {
			word = StringUtillity.toDBC(word.trim().toLowerCase());// 转半角小写无头尾空格
			if (word.length() <= 0 || dfa.isSensitive(word)) {
				continue;
			}
			dfa.addWord(word);
			resultSet.add(word);
		}

		rlist.addAll(resultSet);
	}

	private void redisJs(RedissonClient client, Configuration configuration) throws Exception {
		Version version = Version.defaultVersion;
		RBucket<String> rbucket = client.getBucket("DATA:VERSION");
		if (rbucket.isExists()) {
			String versionString = rbucket.get();
			version = new Version(configuration.MajorVersion, versionString);
		}

		RMap<String, String> rmap = client.getMap("DATA:" + version.toString() + ":MD5");

		// 保存fomula.js文件
		File[] files = FileUtility.getFiles(System.getProperty("user.dir") + "/data/", ".js");
		for (File file : files) {
			String md5 = null;
			try {
				md5 = MD5Utility.getMD5(file);
			} catch (Exception e) {
				e.printStackTrace();
				logger.error("md5解析错误：%s", e.getMessage());
				throw e;
			}
			rmap.put(file.getName(), md5);

			byte[] buffer = FileUtility.read(file);
			String string = new String(buffer, "UTF-8");

			String filename = FileUtility.getFileNameWithoutExtension(file);// 文件名
			RBucket<String> bucket = client.getBucket("DATA:" + version.toString() + ":SCRIPT:" + filename);
			bucket.set(string);
		}
	}

	private void redisData(RedissonClient client, Configuration configuration) throws Exception {
		// 获取文件，检查文件：md5校验，键值变化
		Map<String, String> filesMD5 = new HashMap<>();
		Map<String, File> filesMap = new HashMap<>();

		File[] files = FileUtility.getFiles(System.getProperty("user.dir") + "/data/json/", ".json");
		for (File file : files) {
			String md5 = null;
			try {
				md5 = MD5Utility.getMD5(file);
			} catch (Exception e) {
				e.printStackTrace();
				logger.error("md5解析错误：%s", e.getMessage());
				throw e;
			}
			// 检查内容
			JSONArray dataArray = JSONUtility.readArray(file);
			for (int i = 0; i < dataArray.size(); i++) {
				JSONObject data = dataArray.getJSONObject(i);
				if (!data.containsKey("id")) {
					logger.error("数据不存在id键值:%s", file.getName());
//					throw new Exception();
				}
			}

			filesMD5.put(file.getName(), md5);
			filesMap.put(file.getName(), file);
		}

		Version version = Version.defaultVersion;
		RBucket<String> rbucket = client.getBucket("DATA:VERSION");
		if (rbucket.isExists()) {
			String versionString = rbucket.get();
			version = new Version(configuration.MajorVersion, versionString);
		}
		String _version = version.toString();

		//所有继承DataClass接口的文件
		Set<Class<? extends DataClass>> dataTypeSet = ReflectionUtility.getSubTypesOf("yxy.cherry.data.bean", DataClass.class);
		Map<String, Class<? extends DataClass>> dataTypeMap = new HashMap<>();
		for (Class<? extends DataClass> clazz : dataTypeSet) {
			String name = clazz.getSimpleName();
			dataTypeMap.put(name, clazz);
		}

		// 检查redis库是否有对应的文件md5的key 如果没有属于新建
		RMap<String, String> rmap = client.getMap("DATA:" + version.toString() + ":MD5");


		System.err.println(JSON.toJSONString(dataTypeMap));

		//只更新 MD5值有变动 或 不存在md5表 的json文件
		filesMD5.forEach((String fileName, String md5) -> {
			if (!rmap.isExists() || !rmap.containsKey(fileName) || !md5.equals(rmap.get(fileName).toString())) {
				File file = filesMap.get(fileName);
				String fName = FileUtility.getFileNameWithoutExtension(file);// 文件名
				try {
					if (dataTypeMap.containsKey(fName)) { //继承了DataClass接口的文件
						System.err.println("put file 1::" + fileName);
						HashMap<String, DataClass> dataMap = new HashMap<>();
						Class<? extends DataClass> clazz = dataTypeMap.get(fName);
						JSONArray dataArray = JSONUtility.readArray(file);
						for (int i = 0; i < dataArray.size(); i++) {
							DataClass dc = dataArray.getObject(i, clazz);
							dataMap.put(dc.getCode(), dc);
						}
						String key = "DATA:" + _version + ":DATAS:" + fName;
						client.getMap(key).clear();
						client.getMap(key).putAll(dataMap);
					} else { //非继承了DataClass接口的文件
						System.err.println("put file 2::" + fileName);
						HashMap<Integer, JSONObject> dataMap = new HashMap<>();
						JSONArray dataArray = JSONUtility.readArray(file);
						for (int i = 0; i < dataArray.size(); i++) {
							JSONObject data = dataArray.getJSONObject(i);
							int id = data.getIntValue("id");
							dataMap.put(id, data);
						}
						String key = "DATA:" + _version + ":DATAS:" + fName;
						client.getMap(key).clear();
						client.getMap(key).putAll(dataMap);
					}
					rmap.put(fileName, md5);

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});

		// 版本控制：如果数据表有增减，大版本号更新；如果部分表格结构有变化，中版本号更新；如果结构没有变化，只是数据变化，小版本号更新。

		// TODO: redis key常量化，版本号提升，java文件自动生成

	}

	private void redisData_bak(RedissonClient client, Configuration configuration) throws Exception {
		// 获取文件，检查文件：md5校验，键值变化
		Map<String, String> filesMD5 = new HashMap<>();

		File[] files = FileUtility.getFiles(System.getProperty("user.dir") + "/data/json/", ".json");
		for (File file : files) {
			String md5 = null;
			try {
				md5 = MD5Utility.getMD5(file);
			} catch (Exception e) {
				e.printStackTrace();
				logger.error("md5解析错误：%s", e.getMessage());
				throw e;
			}
			// 检查内容
			JSONArray dataArray = JSONUtility.readArray(file);
			for (int i = 0; i < dataArray.size(); i++) {
				JSONObject data = dataArray.getJSONObject(i);
				if (!data.containsKey("id")) {
					logger.error("数据不存在id键值:%s", file.getName());
//					throw new Exception();
				}
			}

			filesMD5.put(file.getName(), md5);
		}

		Version version = Version.defaultVersion;
		RBucket<String> rbucket = client.getBucket("DATA:VERSION");
		if (rbucket.isExists()) {
			String versionString = rbucket.get();
			version = new Version(configuration.MajorVersion, versionString);
		}

		// 检查redis库是否有对应的文件md5的key 如果没有属于新建
		RMap<String, String> rmap = client.getMap("DATA:" + version.toString() + ":MD5");
		boolean isnew = !rmap.isExists();

		if (isnew) { //全部json文件更新
			rmap.putAll(filesMD5);

			Set<Class<? extends DataClass>> dataTypeSet = ReflectionUtility.getSubTypesOf("yxy.cherry.data.bean", DataClass.class);
			Map<String, Class<? extends DataClass>> dataTypeMap = new HashMap<>();
			for (Class<? extends DataClass> clazz : dataTypeSet) {
				String name = clazz.getSimpleName();
				dataTypeMap.put(name, clazz);
			}

			// 逐个存储文件
			for (File file : files) {
				String filename = FileUtility.getFileNameWithoutExtension(file);// 文件名
				if (dataTypeMap.containsKey(filename)) { //继承了DataClass接口的所有文件
					System.err.println("put file ::" + filename);
					HashMap<String, DataClass> dataMap = new HashMap<>();
					Class<? extends DataClass> clazz = dataTypeMap.get(filename);
					JSONArray dataArray = JSONUtility.readArray(file);
					for (int i = 0; i < dataArray.size(); i++) {
						DataClass dc = dataArray.getObject(i, clazz);
						dataMap.put(dc.getCode(), dc);
					}
					System.out.println("------------------"+filename);
					client.getMap("DATA:" + version.toString() + ":DATAS:" + filename).putAll(dataMap);
				} else { //非继承了DataClass接口的所有文件
					HashMap<Integer, JSONObject> dataMap = new HashMap<>();
					JSONArray dataArray = JSONUtility.readArray(file);
					for (int i = 0; i < dataArray.size(); i++) {
						JSONObject data = dataArray.getJSONObject(i);
						int id = data.getIntValue("id");
						dataMap.put(id, data);
					}
					client.getMap("DATA:" + version.toString() + ":DATAS:" + filename).putAll(dataMap);
				}
			}
			RBucket<String> versionBucket = client.getBucket("DATA:VERSION");
			versionBucket.set(version.toString());
		} else {
			//只更新MD5值有变动的json文件
			filesMD5.forEach((String fileName, String md5) -> {
				//
			});
		}

		// 版本控制：如果数据表有增减，大版本号更新；如果部分表格结构有变化，中版本号更新；如果结构没有变化，只是数据变化，小版本号更新。

		// TODO: redis key常量化，版本号提升，java文件自动生成

	}
	
	public void redisAct(RedissonClient client, Configuration configuration, int areaid) throws Exception {

		System.out.println("活动数据 开始");
		
		// 获取文件，检查文件：md5校验，键值变化
		Map<String, String> filesMD5 = new HashMap<>();

		File[] files = FileUtility.getFiles(System.getProperty("user.dir") + "/data/json/", ".json");
		for (File file : files) {
			String md5 = null;
			try {
				md5 = MD5Utility.getMD5(file);
			} catch (Exception e) {
				e.printStackTrace();
				System.err.println("md5解析错误：%s" + e.getMessage());
				throw e;
			}
			// 检查内容
			JSONArray dataArray = JSONUtility.readArray(file);
			for (int i = 0; i < dataArray.size(); i++) {
				JSONObject data = dataArray.getJSONObject(i);
				if (!data.containsKey("id")) {
					System.err.println("数据不存在id键值:%s" + file.getName());
//					throw new Exception();
				}
			}

			filesMD5.put(file.getName(), md5);
		}

		Version version = Version.defaultVersion;
		String versionkey = String.format("ACT:DATA:%s:%s", areaid, version);
		RBucket<String> rbucket = client.getBucket(versionkey);
		if (rbucket.isExists()) {
			String versionString = rbucket.get();
			version = new Version(configuration.MajorVersion, versionString);
		}

		// 检查redis库是否有对应的文件md5的key 如果没有属于新建
		RMap<String, String> rmap = client.getMap("ACT:DATA:" + areaid + ":" + version.toString() + ":MD5");
		boolean isnew = !rmap.isExists();

		if (isnew) {
			client.getMap("ACT:DATA:" + areaid + ":" + version.toString() + ":MD5").putAll(filesMD5);
			
//			Set<Class<? extends DataClass>> dataTypeSet = ReflectionUtility.getSubTypesOf("yxy.game.pm2.active.data.bean", DataClass.class);
			Set<Class<? extends DataClass>> dataTypeSet = ReflectionUtility.getSubTypesOf("yxy.cherry.data.bean", DataClass.class);
			
			Map<String, Class<? extends DataClass>> dataTypeMap = new HashMap<>();
			for (Class<? extends DataClass> clazz : dataTypeSet) {
				String name = clazz.getSimpleName();
				if(!name.contains("act")) {
					continue;
				}
				
				dataTypeMap.put(name, clazz);
			}

			// 逐个存储文件
			for (File file : files) {
				String filename = FileUtility.getFileNameWithoutExtension(file);// 文件名
				if (dataTypeMap.containsKey(filename)) {
					System.err.println("put file ::" + filename);
					HashMap<String, DataClass> dataMap = new HashMap<>();
					Class<? extends DataClass> clazz = dataTypeMap.get(filename);
					JSONArray dataArray = JSONUtility.readArray(file);
					for (int i = 0; i < dataArray.size(); i++) {
						DataClass dc = dataArray.getObject(i, clazz);
						dataMap.put(dc.getCode(), dc);
					}
					client.getMap("ACT:DATA:" + areaid + ":" + version.toString() + ":DATAS:" + filename).putAll(dataMap);
					continue;
				}

//				HashMap<Integer, JSONObject> dataMap = new HashMap<>();
//				JSONArray dataArray = JSONUtility.readArray(file);
//				for (int i = 0; i < dataArray.size(); i++) {
//					JSONObject data = dataArray.getJSONObject(i);
//					int id = data.getIntValue("id");
//					dataMap.put(id, data);
//				}
//				client.getMap("ACT:DATA:" + areaid + ":" + version.toString() + ":DATAS:" + filename).putAll(dataMap);
			}
			RBucket<String> versionBucket = client.getBucket("ACT:DATA:" + areaid + ":VERSION");
			versionBucket.set(version.toString());
		}

		// 版本控制：如果数据表有增减，大版本号更新；如果部分表格结构有变化，中版本号更新；如果结构没有变化，只是数据变化，小版本号更新。

		// TODO: redis key常量化，版本号提升，java文件自动生成

		System.out.println("活动数据 完成");
	}

	@Override
	public void shutdown() {

	}
}
